package addon;

import java.io.*;

import org.jdom.*;
import org.jdom.input.*;
import java.text.ParseException;
import org.jdom.output.*;

import reservation.v2.Client;
import reservation.v2.NegativeValueException;
import reservation.v2.StartStopException;
import reservation.v2.Trajet;
import reservation.v2.Transport;
import reservation.v2.TypeTransport;
import reservation.v2.Ville;


import java.util.*;

/**
 * Classe permettant de faire la liaison avec le fichier xml contenant la base
 * de données
 * 
 * @author Alexandre Boursier et Jérome Boursier
 * 
 */
public class Database {

    /**
     * Liste des trajets
     */
    protected static List<Trajet> trajets = new LinkedList<Trajet>();

    /**
     * Liste des transports pr
     */
    protected static List<Transport> transports = new LinkedList<Transport>();

    /**
     * Fichier contenant la base de données Le fichier doit être placé à la
     * racine du projet java
     */
    private static final String filename = "Database.xml";

    /**
     * Charger un trajet : l'ajouter à la liste des trajets
     * 
     * @param t
     *            Trajet à charger
     */
    public static void loadTrajet(Trajet t) {
        trajets.add(t);
    }

    /**
     * Charger un transport : l'ajouter à la liste des transports
     * 
     * @param t
     *            Transport à charger
     */
    public static void loadTransport(Transport t) {
        transports.add(t);
    }

    /**
     * Ajouter un nouveau trajet : l'ajouter dans la base de données (xml)
     * l'ajouter à la liste des trajets
     * 
     * @param t
     *            Trajet à ajouter
     */
    public static void addTrajet(Trajet t) {

        // Mettre à jour le fichier xml
        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        // Se placer à l'endroit de l'arborescence où l'on va mettre le nouveau trajet
        Element racine = document.getRootElement();
        Element lesTrajets = racine.getChild("trajets");

        // Créer l'élément correspondant au trajet à insérer
        Element trajet = new Element("trajet");

        // Créer l'élément correspondant a l'identifiant du trajet
        Element idTrajet = new Element("idTrajet");
        idTrajet.setText(new Integer(t.getIdentTrajet()).toString());

        // Ajouter l'identifiant du trajet à l'élément trajet
        trajet.addContent(idTrajet);

        // Créer l'élément correspondant au départ
        Element depart = new Element("depart");
        depart.setText(t.getDepart().toString());

        // Ajouter le départ à l'élement trajet
        trajet.addContent(depart);

        // Créer l'élément correspondant au départ
        Element arrivee = new Element("arrivee");
        arrivee.setText(t.getArrivee().toString());

        // Ajouter le départ à l'élement trajet
        trajet.addContent(arrivee);

        // Créer l'élément correspondant à la date
        Element date = new Element("date");
        date.setText(t.getDate().toString());

        // Ajouter la date à l'élement trajet
        trajet.addContent(date);

        // Créer l'élément correspondant au transport employé pour le trajet
        Element typeTransport = new Element("transportAssocie");
        typeTransport.setText(new Integer(t.getTransport().getIdentTransport()).toString());

        // Ajouter le type de transport à l'élement trajet
        trajet.addContent(typeTransport);

        if (t.getVoyageurs().size() > 0)
        {
            // Créer l'élément correspondant aux voyageurs
            Element voyageurs = new Element("participants");
            trajet.addContent(voyageurs);

            // Créer l'ensemble des voyageurs
            Iterator<Client> it = t.getVoyageurs().iterator();
            while (it.hasNext()) {
                // Traiter le voyageur courant
                Client voyageurCourant = it.next();
                // Créer l'élément correspondant
                Element voyageur = new Element("voyageur");
                // Le rattacher à l'arborescence
                voyageurs.addContent(voyageur);
                // Créer l'identifiant du participant
                Element ident = new Element("identifiant");
                ident.setText(voyageurCourant.getIdentifiant());
                // Le rattacher à l'arborescence
                voyageur.addContent(ident);
                // Créer le nom du participant
                Element nom = new Element("nom");
                nom.setText(voyageurCourant.getNom());
                // Le rattacher à l'arborescence
                voyageur.addContent(nom);
                // Créer le prenom du participant
                Element prenom = new Element("prenom");
                prenom.setText(voyageurCourant.getPrenom());
                // Le rattacher à l'arborescence
                voyageur.addContent(prenom);
            }
        }
        // Ajouter le nouveau trajet dans le fichier
        lesTrajets.addContent(trajet);

        // Sauvegarder le fichier
        try {
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            sortie.output(document, new FileOutputStream(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'écriture dans le fichier !");
            return;
        }

        // Mettre à jour la base de données courante
        trajets.add(t);
    }

    /**
     * Ajouter un participant à un trajet cible et l'inclure dans la base de données (xml)
     * Correspond à la réservation d'un client pour un trajet donné
     * @param t
     *          Le trajet cible
     * @param c
     *          Le client cible
     */
    public static void addVoyageur(Trajet t, Client c)
    {
        // Vérifier si le client est déjà inscrit au trajet demandé
        if(t.exists(c))
        {
            System.out.println("Le client d'identifiant " + c.getIdentifiant() + " est déjà inscrit");
            return;
        }

        // Mettre à jour le fichier xml
        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        // Se placer à l'endroit de l'arborescence où l'on va trouver
        // le trajet à supprimer
        Element racine = document.getRootElement();
        Element lesTrajets = racine.getChild("trajets");

        // Parcourir les trajets de la base
        @SuppressWarnings("unchecked")
        List<Element> listTrajets = lesTrajets.getChildren("trajet");
        Iterator<Element> it = listTrajets.iterator();

        // Vérifier que le nombre de places maximum n'est pas atteint
        if(listTrajets.size() >= t.getTransport().getCapacite())
        {
            System.out.println("Il ne reste plus de place disponible pour ce trajet.");
            return;
        }

        while (it.hasNext()) {
            Element trajetCourant = it.next();

            // Selectionner le trajet courant si c'est celui souhaité
            if (Integer.parseInt(trajetCourant.getChildText("idTrajet")) == t.getIdentTrajet()) 
            {
                Element voyageurs = trajetCourant.getChild("participants");

                // Traiter le cas d'un itinéraire sans voyageurs
                if (t.getVoyageurs().size() == 0)
                { 
                    // Créer l'élément correspondant aux voyageurs
                    voyageurs = new Element("participants");
                    trajetCourant.addContent(voyageurs);
                }

                // Créer l'élément correspondant
                Element voyageur = new Element("voyageur");
                // Le rattacher à l'arborescence
                voyageurs.addContent(voyageur);
                // Créer l'identifiant du participant
                Element ident = new Element("identifiant");
                ident.setText(c.getIdentifiant());
                // Le rattacher à l'arborescence
                voyageur.addContent(ident);
                // Créer le nom du participant
                Element nom = new Element("nom");
                nom.setText(c.getNom());
                // Le rattacher à l'arborescence
                voyageur.addContent(nom);
                // Créer le prenom du participant
                Element prenom = new Element("prenom");
                prenom.setText(c.getPrenom());
                // Le rattacher à l'arborescence
                voyageur.addContent(prenom);

            }  

            // Sauvegarder le fichier
            try {
                XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
                sortie.output(document, new FileOutputStream(filename));
            } catch (Exception e) {
                System.out.println("Erreur lors de l'écriture dans le fichier !");
                return;
            }

        }
        // Ajouter le client à la liste
        t.addVoyageur(c);

    }

    /**

    /**
     * Ajouter un nouveau transport : l'ajouter dans la base de données (xml)
     * l'ajouter à la liste des transports
     * 
     * @param t
     *            Transport à ajouter
     */
    public static void addTransport(Transport t) {

        // Mettre à jour le fichier xml
        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        // Se placer à l'endroit de l'arborescence où l'on va mettre le nouveau transport
        Element racine = document.getRootElement();
        Element lesTransports = racine.getChild("transports");

        // Créer l'élément correspondant au transport à insérer
        Element transport = new Element("transport");

        // Créer l'élément correspondant a l'identifiant du transport
        Element idTransport = new Element("idTransport");
        idTransport.setText(new Integer(t.getIdentTransport()).toString());

        // Ajouter l'identifiant du transport à l'élément transport
        transport.addContent(idTransport);

        // Créer l'élément correspondant au type de transport
        Element categorie = new Element("categorie");
        categorie.setText(t.getCategorie().toString());

        // Ajouter la catégorie à l'élement trajet
        transport.addContent(categorie);

        // Créer l'élément correspondant a l'identifiant du transport
        Element capacite = new Element("capacite");
        capacite.setText(new Integer(t.getCapacite()).toString());

        // Ajouter l'identifiant du transport à l'élément transport
        transport.addContent(capacite);

        // Ajouter le nouveau transport dans le fichier
        lesTransports.addContent(transport);

        // Sauvegarder le fichier
        try {
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            sortie.output(document, new FileOutputStream(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'écriture dans le fichier !");
            return;
        }

        // Mettre à jour la base de données courante
        transports.add(t);
    }


    /**
     * Liste des trajets
     */
    public static String listeTrajets() {
        StringBuilder sb = new StringBuilder();
        for (Trajet t : trajets) {
            sb.append(t.toString());
        }
        return sb.toString();
    }


    /**
     * Liste des transports
     */
    public static String listeTransports() {
        StringBuilder sb = new StringBuilder();
        for (Transport t : transports) {
            sb.append(t.toString());
        }
        return sb.toString();
    }


    /**
     * Récupérer un trajet dans la base de données
     * 
     * @param idTrajet
     *            Identifiant du trajet
     * @return le trajet si il a été trouvé, null sinon
     */
    public static Trajet getTrajet(int t) {
        Iterator<Trajet> iter = Database.trajets.iterator();
        while (iter.hasNext()) {
            Trajet tCourant = iter.next();
            if (tCourant.getIdentTrajet() == t)
                return tCourant;
        }
        return null;
    }


    /**
     * Récupérer un transport dans la base de données
     * 
     * @param idTransport
     *            Identifiant du transport
     * @return le transport si il a été trouvé, null sinon
     */
    public static Transport getTransport(int t) {
        Iterator<Transport> iter = Database.transports.iterator();
        while (iter.hasNext()) {
            Transport tCourant = iter.next();
            if (tCourant.getIdentTransport() == t)
                return tCourant;
        }
        return null;
    }


    /**
     * Supprimer un trajet : dans la base de données (xml) et dans la liste
     * des trajets
     * 
     * @param t
     *           Trajet à supprimer
     */
    public static void supprTrajet(Trajet t) {
        // Mettre à jour le fichier xml
        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        // Se placer à l'endroit de l'arborescence où l'on va trouver
        // le trajet à supprimer
        Element racine = document.getRootElement();
        Element lesTrajets = racine.getChild("trajets");

        // Parcourir les trajets de la base
        @SuppressWarnings("unchecked")
        List<Element> listTrajets = lesTrajets.getChildren("trajet");
        Iterator<Element> it = listTrajets.iterator();
        while (it.hasNext()) {
            Element trajetCourant = it.next();
            // Supprimer le trajet si c'est celui souhaité
            if (Integer.parseInt(trajetCourant.getChildText("idTrajet")) == t.getIdentTrajet()) {
                trajetCourant.removeContent();
            }
        }

        // Sauvegarder le fichier
        try {
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            sortie.output(document, new FileOutputStream(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'écriture dans le fichier !");
            return;
        }

        // Mettre à jour la base de données courante
        trajets.remove(t);

    }


    /**
     * Supprimer un transport : dans la base de données (xml) et dans la liste
     * des transports
     * 
     * @param t
     *           Transport à supprimer
     */
    public static void supprTransport(Transport t) {
        // Mettre à jour le fichier xml
        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        // Se placer à l'endroit de l'arborescence où l'on va trouver
        // le transport à supprimer
        Element racine = document.getRootElement();
        Element lesTransports = racine.getChild("transports");

        // Parcourir les épreuves de la base
        @SuppressWarnings("unchecked")
        List<Element> listTransports = lesTransports.getChildren("transport");
        Iterator<Element> it = listTransports.iterator();
        while (it.hasNext()) {
            Element transportCourant = it.next();
            // Supprimer le transport si c'est celui souhaité
            if (Integer.parseInt(transportCourant.getChildText("idTransport")) == t.getIdentTransport()) {
                lesTransports.removeContent();
            }
        }

        // Sauvegarder le fichier
        try {
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            sortie.output(document, new FileOutputStream(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'écriture dans le fichier !");
            return;
        }

        // Mettre à jour la base de données courante
        transports.remove(t);
    }


    /**
     * Charger une base de données depuis un fichier xml
     * @throws NegativeValueException 
     * @throws StartStopException 
     * @throws IOException 
     * @throws NullPointerException 
     * @throws NumberFormatException 
     */
    public static void load() throws NegativeValueException, ParseException, NumberFormatException, NullPointerException, IOException, StartStopException  {

        // Réinitialiser toutes les listes
        Database.trajets.clear();
        Database.transports.clear();

        SAXBuilder sxb = new SAXBuilder();
        Document document;

        try {
            document = sxb.build(new File(filename));
        } catch (Exception e) {
            System.out.println("Erreur lors de l'ouverture du fichier !");
            return;
        }

        Element racine = document.getRootElement();

        // Traiter les trajets
        Element lesTrajets = racine.getChild("trajets");
        @SuppressWarnings("unchecked")
        List<Element> listTrajets = lesTrajets.getChildren("trajet");
        Iterator<Element> itTrajet = listTrajets.iterator();

        while (itTrajet.hasNext()) {

            // Traiter le trajet courant
            Element trajetCourant = itTrajet.next();

            // Récupérer l'identifiant du trajet
            int idTrajet = Integer.parseInt(trajetCourant.getChildText("idTrajet"));

            // Récupérer le départ du trajet
            String depart = trajetCourant.getChildText("depart");

            // Récupérer l'arrivée du trajet
            String arrivee = trajetCourant.getChildText("arrivee"); 

            // Récupérer l'arrivée du trajet
            String date = trajetCourant.getChildText("date"); 

            // Recupérer le transport 
            Transport tra = Database.getTransport
                    (Integer.parseInt(trajetCourant.getChildText("transportAssocie")));

            // Récupérer l'ensemble des voyageurs
            @SuppressWarnings("unchecked")
            List<Element> listVoyageurs = trajetCourant.getChild("participants").getChildren("participant");
            Iterator<Element> itVoyageurs = listVoyageurs.iterator();
            LinkedList<Client> clients = new LinkedList<Client>();
            while (itVoyageurs.hasNext()) {

                // Traiter le voyageur courant
                Element voyageurCourant = itVoyageurs.next();

                // Récupérer l'identifiant du participant
                String ident = voyageurCourant.getChildText("identifiant");

                // Récupérer le nom du participant
                String nom = voyageurCourant.getChildText("nom");

                // Récupérer le prénom du constructeur
                String prenom = voyageurCourant.getChildText("prennom");

                // Ajouter le voyageur à la liste
                clients.add(new Client(ident, nom, prenom));
            }

            // Enregistrer le trajet
            Trajet tr = new Trajet(idTrajet,Ville.valueOf(depart),
                    Ville.valueOf(arrivee), Trajet.toFormatedDate(date), tra, clients);

            Database.loadTrajet(tr);
        } 

        // Traiter les transports
        Element lesTransports = racine.getChild("transports");
        @SuppressWarnings("unchecked")
        List<Element> listTransports = lesTransports.getChildren("transport");
        Iterator<Element> itTransport = listTransports.iterator();

        while (itTransport.hasNext()) {

            // Traiter le transport courant
            Element transportCourant = itTransport.next();

            // Récupérer l'identifiant du transport
            int idTransport = Integer.parseInt(transportCourant.getChildText("idTransport"));

            // Récupérer le type du transport
            String categorie = transportCourant.getChildText("categorie");

            // Récupérer la capacite du transport
            int capacite = Integer.parseInt(transportCourant.getChildText("capacite")); 

            // Enregistrer le transport
            Transport tr = new Transport(idTransport,TypeTransport.valueOf(categorie),capacite);

            Database.loadTransport(tr);
        }
    }
}
